package com.example.science.interceptor;


import com.example.science.exception.NoLoginException;
import com.example.science.util.JwtUtil;
import com.example.science.util.RedisKey;
import com.example.science.util.RedisUtil;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Resource
    private RedisUtil redisUtil;
    private ThreadLocal<Boolean> context = new ThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("请求进入拦截器");
        if(request.getMethod().equals("OPTIONS")){
            return true;
        }
        String access = request.getHeader("token");
        //2.判断access是否过期
        if(access == null){
            throw new NoLoginException();
        }
        //避免多线程操作问题(加锁)
        synchronized (access.intern()){
            //3.判断redis中access：expire键是否存在
            if(!redisUtil.exists(RedisKey.accessExpire(access))){
                //4.说明access过期了
                //5.从redis中取出refresh
                String refresh = redisUtil.getString(RedisKey.userRefresh(access));
                if(refresh==null){
                    throw new NoLoginException();
                }
                //6.为避免临界值问题，在refresh剩余5分钟时间过期时，让它提前过期或者刷新一次access
                long expireTime = JwtUtil.parseToken(refresh).getExpiration().getTime();
                long now = new Date().getTime();
                if(expireTime-now<300000){
                    //不足5分钟提前过期
                    throw new NoLoginException();
                }
                //7.刷新access(续期操作)
                Claims claims = JwtUtil.parseToken(refresh);
                HashMap<String, Object> map = new HashMap<>();
                int userId = Integer.parseInt(claims.get("userId").toString());
                map.put("userId",claims.get("userId"));
                map.put("userName", claims.get("userName"));
                map.put("userNickname", claims.get("userNickname"));
                map.put("userPhone", claims.get("userPhone"));
                map.put("userRole", claims.get("userRole"));
                //8.生成新的access和refresh
                String newAccess = UUID.randomUUID().toString().replaceAll("-", "");
                String newRefresh = JwtUtil.createToken(map, 24*60);
                redisUtil.setString(RedisKey.accessExpire(access),String.valueOf(userId),10);
                    //删除在并发时之前产生的数据
    //            String deleteAccess = redisUtil.getString(RedisKey.userAccess(userId));
    //                if(deleteAccess!=null&&!access.equals(deleteAccess)){
    //                    redisUtil.del(RedisKey.accessExpire(deleteAccess));
    //                    redisUtil.del(RedisKey.userRefresh(deleteAccess));
    //                }
                //使用用户id为键，保存了目前正在使用的access值
                redisUtil.setString(RedisKey.userAccess(userId), newAccess,3*24*60*60);
                //使用access为键，保存了access过期时间
                redisUtil.setString(RedisKey.accessExpire(newAccess),userId+"",5*60);
                //使用目前最新的access作为键保存了refresh
                redisUtil.setString(RedisKey.userRefresh(newAccess),newRefresh,3*24*60*60);
                //9.将access响应给客户端
                response.setHeader("token", newAccess);
                response.setHeader("Access-Control-Expose-Headers","token");
                context.set(Boolean.TRUE);
            }
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView){
//        if(response.getHeader("token")!=null){
//            String access = response.getHeader("token");
//            redisUtil.del(RedisKey.userRefresh(access));
//        }

        if(context.get()!=null&& context.get()){
            //清除掉容器里面的数据
            context.remove();
            String access = request.getHeader("token");
            System.out.println("准备删除");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("进入删除");
                        Thread.sleep(8000);

                        redisUtil.del(RedisKey.userRefresh(access));
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    }

}
